/* memread.cpp
 *
 * try to maximize bandwidth by bombarding with memcpy
 *
 * working set size <= cache size
 */

#include <omp.h>
#include "bench.h"
#include <assert.h>
#include <iostream>

#include <unistd.h>
#include <string.h>
#include "papi_test.h"
#include "events.h"
#include <sched.h>
#include <sys/types.h>
#include <sys/syscall.h>

#define AI 0

int base = 0;
extern float diffTime(struct timeval before, struct timeval after);
extern void recordTime(struct timeval& current);

float stride(int* src, int startindex, int endindex, long num_accesses, int64_t block_size)
{
    //std::cout << "startindex : " << startindex << " endindex : " << endindex << " num_accesses : " << num_accesses << std::endl;
    int sum = 0;
    int index = startindex;
    int block_len = block_size / sizeof(int);
	int retval;
	long long values;
    double final;
	int EventSet = PAPI_NULL;
    PAPI_event_info_t evinfo;
	char event_name[PAPI_MAX_STR_LEN];
    struct timeval before, after;

    // set thread affinity to cpu
    cpu_set_t mask;

    CPU_ZERO(&mask);
    int tid = omp_get_thread_num();
    CPU_SET(tid, &mask);
    if ( (retval = sched_setaffinity(syscall(SYS_gettid), sizeof(mask), &mask)) != 0)
    {
        printf("set affinity failed\n");
        exit(0);
    }

    if ( ( retval = PAPI_create_eventset( &EventSet ) ) != PAPI_OK )
        test_fail( __FILE__, __LINE__, "PAPI_create_eventset", retval );
    /*
    for (int j = 0; eventlist[j] != 0; j++)
    {
        retval = PAPI_event_code_to_name( eventlist[j], event_name );
        printf("%s\t", event_name);
    }
    printf("\n");
    */

    float time = -1;
    int num_events = 0;
    for (int j = 0; eventlist[j] != 0; j++)
    {
        retval = PAPI_event_code_to_name( eventlist[j], event_name );

        if ( retval != PAPI_OK )
            test_fail( __FILE__, __LINE__, "PAPI_event_code_to_name", retval );

        if ( (retval = PAPI_add_event( EventSet, eventlist[j] ) ) != PAPI_OK )
            continue;

        if ( (retval = PAPI_get_event_info( eventlist[j], &evinfo )) != PAPI_OK )
            test_fail( __FILE__, __LINE__, "PAPI_get_event_info", retval );

        if (tid == 1000)
            printf( "\nEvent: %s\nShort: %s\nLong: %s\n\n",
                    evinfo.symbol, evinfo.short_descr, evinfo.long_descr );

        PAPI_start(EventSet);
        if ( ( retval = PAPI_reset( EventSet ) ) != PAPI_OK )
            test_fail( __FILE__, __LINE__, "PAPI_reset", retval );


        recordTime(before);
        volatile int dest;
        int local[block_len];
        for (long i = 0; i < num_accesses; i++)
        {
            // copy the block of memory
            //memcpy(local, src + index, block_size);
            dest = src[index];

            index += dest;
            if (index > endindex || base)
                index = startindex;
        }
        recordTime(after);
        if ( ( retval = PAPI_read( EventSet, &values ) ) != PAPI_OK )
            test_fail( __FILE__, __LINE__, "PAPI_read", retval );

        PAPI_stop(EventSet, NULL);
        if ( ( retval =
                    PAPI_remove_event( EventSet, eventlist[j] ) ) != PAPI_OK )
            test_fail( __FILE__, __LINE__, "PAPI_remove_event", retval );

        final = values;
        if (tid == 0)
            printf("%e\t", final);

        time += diffTime(before, after);
        num_events++;
        fflush(NULL);
    }
    //printf("\n");
    PAPI_unregister_thread();
    finaldest[tid] = sum;
    return time/(float)num_events;
}
